package umacis.lwuitcomponent;

import java.io.IOException;
import java.util.Vector;

import com.sun.lwuit.Button;
import com.sun.lwuit.Component;
import com.sun.lwuit.Container;
import com.sun.lwuit.Form;
import com.sun.lwuit.Image;
import com.sun.lwuit.Label;
import com.sun.lwuit.animations.CommonTransitions;
import com.sun.lwuit.events.ActionEvent;
import com.sun.lwuit.events.ActionListener;
import com.sun.lwuit.layouts.BorderLayout;
import com.sun.lwuit.layouts.BoxLayout;
import com.sun.lwuit.plaf.Style;



//A composite component is really a Container which we would assemble into a specialized component by tailoring together smaller components. Naturally we could also perform custom painting for such a composite but that often defeats the purpose...
//
//These components aren't suitable for everything but for things such as a tree we gain several huge advantages by using Container nesting to build a tree:
//
//    * We represent the nodes using custom components allowing subclasses to manipulate the look of the tree to a great degree.
//    * We hardly need to write any code and styles, touch support, keyboard navigation etc. are all built in
//    * We can leverage transitions and component nesting for really stunning effects
//
//The tree uses a simple model component with some dummy data, mapping it to a filesystem or any other such element is trivial. All you need to do is implement TreeModel appropriately and it only contains 2 methods (I spent allot of time with the Swing tree model...). Its not as powerful as the Swing model since it doesn't support editing or mutation (from the model) but these are relatively easy to add. It took me roughly 2 hours to write everything including this post, it was so long because I discovered a minor LWUIT bug in scrolling/animation that is triggered when a tree starts off small and ends up being rather large.
//
//Lets go strait to the code, first we need the model:

/**
* Arranges tree node objects, a node can essentially be anything
*
* @author Shai Almog
*/
interface TreeModel {
   /**
    * Returns the child objects representing the given parent, null should return
    * the root objects
    */
   public Vector getChildren(Object parent);

   /**
    * Is the node a leaf or a folder
    */
   public boolean isLeaf(Object node);
}

//I think thats pretty self explanitory...
//Then we would like the implementation:

class Node {
Object[] children;
String value;

public Node(String value, Object[] children) {
   this.children = children;
   this.value = value;
}

public String toString() {
   return value;
}
}

//TreeModel model = new TreeModel() {
//Node[] sillyTree =  {
//   new Node("Root 1", new Node[] {
//       new Node("Child 1", new Node[] {
//           new Node("Gand Child 1", new Node[] {
//           }),
//           new Node("Gand Child 2", new Node[] {
//           }),
//           new Node("Gand Child 3", new Node[] {
//           }),
//           new Node("Gand Child 4", new Node[] {
//           }),
//       }),
//       new Node("Child 2", new Node[] {
//           new Node("Something Else", new Node[] {
//           }),
//           new Node("More of the same", new Node[] {
//           }),
//       }),
//       new Node("Child 3", new Node[] {
//       }),
//       new Node("Child 4", new Node[] {
//       }),
//   }),
//   new Node("Root 2", new Node[] {
//       new Node("Something Else", new Node[] {
//       }),
//       new Node("More of the same", new Node[] {
//       }),
//   }),
//   new Node("Root 3", new Node[] {
//       new Node("Something Else", new Node[] {
//       }),
//       new Node("More of the same", new Node[] {
//       }),
//   }),
//   new Node("Root 4", new Node[] {
//   }),
//};

//Thats a bit long and tedious but its mostly unrelated to LWUIT, the main code is the tree component code which is really rather simple:

/**
* LWUIT Tree component sample
*
* @author Shai Almog
*/
public class TreeComponent extends Container {
   private static final String KEY_OBJECT = "TREE_OBJECT";
   private static final String KEY_PARENT = "TREE_PARENT";
   private static final String KEY_EXPANDED = "TREE_NODE_EXPANDED";
   private static final String KEY_DEPTH = "TREE_DEPTH";

   private ActionListener expansionListener = new ActionListener() {
       public void actionPerformed(ActionEvent evt) {
           Component c = (Component)evt.getSource();
           Object e = c.getClientProperty(KEY_EXPANDED);
           if(e != null && e.equals("true")) {
               collapseNode(c);
           } else {
               expandNode(c);
           }
       }
   };
   private TreeModel model;
   private Image folder;
   private Image nodeImage;
   private int depthIndent = 15;

   public TreeComponent(TreeModel model) {
       try {
           this.model = model;
           folder = Image.createImage("/folder.png");
           nodeImage = Image.createImage("/page_white.png");
           setLayout(new BoxLayout(BoxLayout.Y_AXIS));
           buildBranch(null, 0, this);
           setScrollableY(true);
       } catch (IOException ex) {
           ex.printStackTrace();
       }
   }

   private void expandNode(Component c) {
       c.putClientProperty(KEY_EXPANDED, "true");
       int depth = ((Integer)c.getClientProperty(KEY_DEPTH)).intValue();
       Container parent = c.getParent();
       Object o = c.getClientProperty(KEY_OBJECT);
       Container dest = new Container(new BoxLayout(BoxLayout.Y_AXIS));
       Label dummy = new Label();
       parent.addComponent(BorderLayout.CENTER, dummy);
       buildBranch(o, depth, dest);
       parent.replace(dummy, dest, CommonTransitions.createSlide(CommonTransitions.SLIDE_VERTICAL, true, 300));
   }

   private void collapseNode(Component c) {
       c.putClientProperty(KEY_EXPANDED, null);
       Container p = c.getParent();
       for(int iter = 0 ; iter < p.getComponentCount() ; iter++) {
           if(p.getComponentAt(iter) != c) {
               Label dummy = new Label();
               p.replaceAndWait(p.getComponentAt(iter), dummy, CommonTransitions.createSlide(CommonTransitions.SLIDE_VERTICAL, false, 300));
               p.removeComponent(dummy);
           }
       }
   }

   /**
    * Adds the child components of a tree branch to the given container.nt
    */
   protected void buildBranch(Object parent, int depth, Container destination) {
       Vector children = model.getChildren(parent);
       int size = children.size();
       Integer depthVal = new Integer(depth + 1);
       for(int iter = 0 ; iter < size ; iter++) {
           Object current = children.elementAt(iter);
           Button nodeComponent = createNodeComponent(current, depth);
           if(model.isLeaf(current)) {
               destination.addComponent(nodeComponent);
           } else {
               Container componentArea = new Container(new BorderLayout());
               componentArea.addComponent(BorderLayout.NORTH, nodeComponent);
               destination.addComponent(componentArea);
               nodeComponent.addActionListener(expansionListener);
           }
           nodeComponent.putClientProperty(KEY_OBJECT, current);
           nodeComponent.putClientProperty(KEY_PARENT, parent);
           nodeComponent.putClientProperty(KEY_DEPTH, depthVal);
       }
   }

   protected Button createNodeComponent(Object node, int depth) {
       Button cmp = new Button(node.toString());
       if(model.isLeaf(node)) {
           cmp.setIcon(nodeImage);
       } else {
           cmp.setIcon(folder);
       }
       updateNodeComponentStyle(cmp.getSelectedStyle(), depth);
       updateNodeComponentStyle(cmp.getUnselectedStyle(), depth);
       return cmp;
   }

   protected void updateNodeComponentStyle(Style s, int depth) {
       s.setBorder(null);
       s.setPadding(LEFT, depth * depthIndent);
   }
}


//Then all we need is to use it in order to produce the video on the right:

//Form treeForm = new Form("Tree Test");
//treeForm.setLayout(new BorderLayout());
//TreeComponent tree = new TreeComponent(model);
//treeForm.addComponent(BorderLayout.CENTER, tree);
//treeForm.setScrollable(false);
//treeForm.show();
//
//
//Read more: http://lwuit.blogspot.com/search?q=treecomponent#ixzz0N1hfqFUy
